iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 4
0

本篇重點

ASP.NTE Core 下的Middleware是如何運作的

前言

Middleware 中文叫做中介軟體
是一種用來溝通應用端程式以及資料端程式的橋梁
依照各種不同的功能面會有不同的 Middleware
各司其職,有效分工去增加效率


ASP .NET Core 的 Middleware

ASP .NET Core 使用 Middleware 去執行個個狀態所要做的事情:

  1. 利用組件 component 的方式透過 pipeline 執行下一個 Middleware ,也可以透過next()方法直接執行下一個 Middleware ,而非依序執行

  2. 所謂的 Middleware 其實就是一個 class ,可以在這些物件裡添加邏輯, ASP .NET Core 已經有提供現有的Middleware提供我們使用

透過 pipeline(管線)的方式去執行,可以把他想成一座工廠,依照每個產線的流程依序去完成產品的製作,另外在 pipeline 的模式理會符合先進後出原則

舉個例子
還記得以前小時候在麥當勞打工的時候
櫃檯的要求是一個餐點必須在1分鐘完成
當時的流程大概是

點餐-> 壓飲料->拿漢堡>拿薯條>拿飲料>包裝結帳

(題外話這是早期的版本,後來改成現點現做,採用單一職責分工,不是都由一個服務生完成)

可以看到其實這就是先進後出的概念

點餐 - 包裝結帳
壓飲料 - 拿飲料
拿漢堡&拿薯條

回到 .NET Core的例子
在上篇提到的Startup.cs裡面的Configure方法
裡面會依序執行Middleware
也可以透過一些方法去操作Middleware的行為,例如next()


.NET Core 下的Middleware

專案範本的Middleware

使用 Cli 建立的 MVC 預設專案範本,會自動寫入幾項目前微軟提供的 Middleware 在 Startup.cs 裡面,這些 Middleware 都已經先封裝成一個靜態方法,並已UseXXXX命名,另外也可以在 UseRouting() 和 UseEndpoint() 裡新增一些客制的 Middleware

  • DeveloperExceptionPage()
    這個是用來區別開發環境以及正式環境所需要顯示的內容,通常在開發環境我們希望可以顯示更詳細的錯誤程式碼在我們的頁面上,但在正式環境時,就希望統一頁面訊息,顯示一個 error page 就好,這邊會根據你的環境是否是開發環境或是正式環境去做判斷,也就是上一篇提到的 launchSettings.json
  • UseRouting()
    提供 Router 的 component ,必須先註冊後才能再 Endpoint Routing Middleware裡面去做調整
  • UseHttpsRedirection()
    將 Http 轉成 https 的 Middleware
  • UseStaticFiles()
    處理靜態檔案,後面會再討論 Core 如何處理 staticFile
  • UseRouting()
    確認 request 來的 router 是否有符合,有點像執行 UseEndpoints() 前的設定動作
  • UseAuthorization()
    處理授權的 Middleware , 在進入網頁會先檢查身分認證
  • UseEndpoints()
    在這個步驟會使用 Map 的方法,根據 UseRouting 裡面所註冊的路由,去執行後續的動作

IApplicationBuilder

ASP .NET Core 的 IApplicationBuilder 是用來處理 pipeline 運作
其實就是一連串的HttpContext 去做request 和 response 的操作
其中也提供幾個方法,可以用來註冊各個 Middleware

  • use
    用來註冊 Middleware 的方法
  • run
    和use相同,只差在不會依序執行下一個 Middleware
  • next()
    跳至下一個Middleware的指令
  • MapGet and Map
    這兩個方法都是處理HTTP的請求,差別只在MapGet 只包含Get, 而Map則包含HTTP的各種請求 (GET/POST/PUT/DELETE)

Deom

此部分會實作兩個例子,使用 IApplicationBuilder 方法去操作 Middleware 以及封裝 Middleware

使用 IApplicationBuilder 方法

使用一開始提到麥當勞的例子,示範的程式碼如下

前面有提到 Use 可以串接下一個 Middleware,在這裡先來看看 Use 的語法
輸入一個泛型的委派,第一個參數是 httpContext context,可以接收 Http 的 request 和 reaponse ,第二個參數是一個 function ,用來指向下一個 Middleware 的方法 next()

Use()

 //order Middleware at counter
app.Use(async (context, next) =>
{
    await context.Response.WriteAsync("Welcome to McDonald's, Would you like to order? \n");
    await context.Response.WriteAsync("You order a Big Mac meal, it's 9.65 SGD \n");
    await next();
    await context.Response.WriteAsync("here is your receipt and meal, Thank you for coming.\n");
});

至於 Run() 這個方法,就只有傳入一個 httpContext context
透過委派的方式去傳遞,不會串連各個 Middleware

Run()

app.Run(async (context) =>
{
   await context.Response.WriteAsync("Preparing and paking fries \n");
});

執行結果

封裝 Middleware

在範本裡面微軟提供的 Middleware
大部分都已經封裝成一個類別,這樣也比較易於管理且程式碼也較簡潔

在這邊新增一個 DrinkMiddleware.cs 去封裝飲料的 Midleware
根據 VS2019 可以直接在專案新增一個Middleware的 cs
新增後將 Invoke 方法改成一個非同步的方法
將原先飲料部分的邏輯加入到這個方法裡面

startup.cs

//drink bar
app.Use(async (context, next) =>
{
   await context.Response.WriteAsync("Making a drink \n");
   await next();
   await context.Response.WriteAsync("Taking a drink \n");

});

將這段調整到Invoke方法內

DrinkMiddleware.cs

// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
    public class DrinkMiddleware
    {
        private readonly RequestDelegate _next;

        public DrinkMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            await httpContext.Response.WriteAsync("Making a drink \n");
            await _next(httpContext);
            await httpContext.Response.WriteAsync("Taking a drink \n");
        }
    }

    // Extension method used to add the middleware to the HTTP request pipeline.
    public static class DrinkMiddlewareExtensions
    {
        //UseDrinkMiddleware 是自己定義的,也就是等等app所呼叫的方法
        public static IApplicationBuilder UseDrinkMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<DrinkMiddleware>();
        }
    }

如此一來在 startup.cs 就可以直接使用這個類別
這樣也可以在擴充自己要增加的邏輯
此產生的結果和前一個顯示是相同的


參考資料:

https://zhuanlan.zhihu.com/p/112507925

https://stackoverflow.com/questions/57846127/what-are-the-differences-between-app-userouting-and-app-useendpoints

https://aregcode.com/blog/2019/dotnetcore-understanding-aspnet-endpoint-routing/

委派介紹

async 與 await


上一篇
Day 3 ASP.NET Core生命週期
下一篇
Day 5 Dependency Injection
系列文
ASP.NET Core 入門實戰30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言